Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports Microsoft.Win32
Imports System.Globalization
Imports System.Runtime.InteropServices
Imports MefTZAddin.interop.types

Namespace interop
    Public Module TimeZoneInterop
        Public Sub SetTimeZone(ByVal tzi As TimeZoneInfo)
            SetTimeZone(ConvertToTimeZoneInformation(tzi))
        End Sub

        ''' <summary>
        ''' Sets new time-zone information for the local system.
        ''' </summary>
        ''' <param name="tzi">Struct containing the time zone parameters to set.</param>
        Public Sub SetTimeZone(ByVal tzi As TimeZoneInformation)
            ' Setting time zone is filtered from token by default
            AdjustPrivileges.SetTokenPrivilege(AdjustPrivileges.SE_TIME_ZONE_NAME)
            Win32.CheckWin32Error()

            ' Set local system time zone
            If SetTimeZoneInformation(tzi) Then
                Win32.CheckWin32Error()
            Else
                CultureInfo.CurrentCulture.ClearCachedData()
                TimeZoneInfo.ClearCachedData()
                SendMessageInterop.BroadcastSettingsChange()
            End If

            ' Setting time zone is filtered from token by default
            AdjustPrivileges.UnsetTokenPrivilege(AdjustPrivileges.SE_TIME_ZONE_NAME)
            Win32.CheckWin32Error()
        End Sub

        Public Function ConvertToTimeZoneInformation(ByVal tzi As TimeZoneInfo) As TimeZoneInformation
            Dim cpy As New TimeZoneInformation()
            cpy.bias = tzi.BaseUtcOffset.TotalMinutes

            Dim rules As TimeZoneInfo.AdjustmentRule() = tzi.GetAdjustmentRules()

            If tzi.SupportsDaylightSavingTime AndAlso rules.Length > 0 Then
                cpy.daylightName = tzi.DaylightName
                cpy.daylightBias = rules(0).DaylightDelta.TotalMinutes

                For Each rule In rules
                    If DateTime.Now >= rule.DateStart AndAlso DateTime.Now <= rule.DateEnd Then
                        cpy.daylightDate = rule.DaylightTransitionStart.ToSystemTime()
                        cpy.standardDate = rule.DaylightTransitionEnd.ToSystemTime()
                        Exit For
                    End If
                Next
                cpy.daylightDate = rules(0).DateStart.ToSystemTime()
            Else
                cpy.daylightName = ""
            End If

            cpy.standardName = tzi.StandardName

            Return cpy
        End Function

        ''' <summary>
        ''' Gets current time zone information for the local system.
        ''' </summary>
        ''' <returns>Struct containing the current time zone parameters.</returns>
        Public Function GetTimeZone() As TimeZoneInformation
            ' create struct instance
            Dim tzi As TimeZoneInformation = Nothing

            ' retrieve timezone info
            Dim currentTimeZone As Integer = GetTimeZoneInformation(tzi)

            Return tzi
        End Function


        ''' <summary>
        ''' [Win32 API call]
        ''' The GetTimeZoneInformation function retrieves the current time-zone parameters.
        ''' These parameters control the translations between Coordinated Universal Time (UTC)
        ''' and local time.
        ''' </summary>
        ''' <param name="lpTimeZoneInformation">[out] Pointer to a TIME_ZONE_INFORMATION structure to receive the current time-zone parameters.</param>
        ''' <returns>
        ''' If the function succeeds, the return value is one of the following values.
        ''' <list type="table">
        ''' <listheader>
        ''' <term>Return code/value</term>
        ''' <description>Description</description>
        ''' </listheader>
        ''' <item>
        ''' <term>TIME_ZONE_ID_UNKNOWN == 0</term>
        ''' <description>
        ''' The system cannot determine the current time zone. This error is also returned if you call the SetTimeZoneInformation function and supply the bias values but no transition dates.
        ''' This value is returned if daylight saving time is not used in the current time zone, because there are no transition dates.
        ''' </description>
        ''' </item>
        ''' <item>
        ''' <term>TIME_ZONE_ID_STANDARD == 1</term>
        ''' <description>
        ''' The system is operating in the range covered by the StandardDate member of the TIME_ZONE_INFORMATION structure.
        ''' </description>
        ''' </item>
        ''' <item>
        ''' <term>TIME_ZONE_ID_DAYLIGHT == 2</term>
        ''' <description>
        ''' The system is operating in the range covered by the DaylightDate member of the TIME_ZONE_INFORMATION structure.
        ''' </description>
        ''' </item>
        ''' </list>
        ''' If the function fails, the return value is TIME_ZONE_ID_INVALID. To get extended error information, call GetLastError.
        ''' </returns>
        <DllImport("kernel32.dll", CharSet:=CharSet.Auto)> _
        Private Function GetTimeZoneInformation(ByRef lpTimeZoneInformation As TimeZoneInformation) As Integer
        End Function

        ''' <summary>
        ''' [Win32 API call]
        ''' The SetTimeZoneInformation function sets the current time-zone parameters.
        ''' These parameters control translations from Coordinated Universal Time (UTC)
        ''' to local time.
        ''' </summary>
        ''' <param name="lpTimeZoneInformation">[in] Pointer to a TIME_ZONE_INFORMATION structure that contains the time-zone parameters to set.</param>
        ''' <returns>
        ''' If the function succeeds, the return value is nonzero.
        ''' If the function fails, the return value is zero. To get extended error information, call GetLastError.
        ''' </returns>
        <DllImport("kernel32.dll", CharSet:=CharSet.Auto)> _
        Private Function SetTimeZoneInformation(<[In]()> ByRef lpTimeZoneInformation As TimeZoneInformation) As Boolean
        End Function

    End Module
End Namespace
